import { RedisKeyEnum } from '@/common/enums/redis-key.enum';
import { LoggerService } from '@/common/logger/logger.service';
import { RedisService } from '@/common/redis/redis.service';
import { handleError } from '@/utils/handle.error';
import { HttpService } from '@nestjs/axios';
import { BadRequestException, Injectable } from '@nestjs/common';
import { firstValueFrom } from 'rxjs';
import { DailyRoutineDto } from './dto/daily-routine.dto';
import { Request } from 'express';
import { TUser } from '@/@types/common';
import { Cacheable } from '@/common/decorators/cache.decorator';
import { compareTime, today } from '@/utils/date';

@Injectable()
export class FeishuService {
  private readonly BASE_URL = 'https://open.feishu.cn/open-apis';
  private readonly app_id = 'cli_a73e925ad87ed01c'
  private readonly app_secret = 'Xr3gFbPnQrix5Pcw3h17Ydbr4ZlvkdAr'
  private readonly spreadsheet_token = 'DkQfsbmGRh9eAZtOeorcgQBUnBf'

  constructor(
    private readonly logger: LoggerService,
    private readonly httpService: HttpService,
    private readonly redisService: RedisService
  ) {
    this.logger.setContext(FeishuService.name)
  }

  /** 1. 获取 Tenant Access Token */
  async getTenantAccessToken(appId: string, appSecret: string): Promise<string> {
    try {
      this.logger.log(`${this.getTenantAccessToken.name} was called`);
      const url = `${this.BASE_URL}/auth/v3/tenant_access_token/internal`;
      const response = await firstValueFrom(
        this.httpService.post(url, { app_id: appId, app_secret: appSecret }),
      );
      this.redisService.setex(RedisKeyEnum.FeishuKey + appId + appSecret, response.data.expire, response.data.tenant_access_token)
      return response.data.tenant_access_token;
    } catch (error) {
      handleError(this.logger, error, {
        common: '获取Tenant Access Token失败'
      })
    }
  }

  /** 获取电子表格token信息 spreadsheetToken: 表格尾部后缀 */
  @Cacheable(RedisKeyEnum.FeishuKey)
  async getExcelInfo(spreadsheetToken: string, tenantAccessToken: string) {
    try {
      this.logger.log(`${this.getExcelInfo.name} was called`);
      const url = `${this.BASE_URL}/sheets/v3/spreadsheets/${spreadsheetToken}`;
      const response = await firstValueFrom(
        this.httpService.get(url, {
          headers: { Authorization: `Bearer ${tenantAccessToken}` },
        }),
      );
      console.log(response, 'response')
      return response.data.data;
    } catch (error) {
      handleError(this.logger, error, {
        common: '获取电子表格信息失败'
      })
    }
  }

  /** 2. 获取电子表格所有工作表 */
  @Cacheable(RedisKeyEnum.FeishuKey)
  async getSheetInfo(spreadsheetToken: string, tenantAccessToken: string) {
    try {
      this.logger.log(`${this.getSheetInfo.name} was called`);
      const url = `${this.BASE_URL}/sheets/v3/spreadsheets/${spreadsheetToken}/sheets/query`;
      const response = await firstValueFrom(
        this.httpService.get(url, {
          headers: { Authorization: `Bearer ${tenantAccessToken}` },
        }),
      );
      return response.data.data.sheets;
    } catch (error) {
      handleError(this.logger, error, {
        common: '获取电子表格所有工作表信息失败'
      })
    }
  }

  /** 3. 读取电子表格数据 */
  async readSheetData(spreadsheetToken: string, range: string, tenantAccessToken: string) {
    try {
      this.logger.log(`${this.readSheetData.name} was called`);
      const url = `${this.BASE_URL}/sheets/v2/spreadsheets/${spreadsheetToken}/values/${range}`;
      const response = await firstValueFrom(
        this.httpService.get(url, {
          headers: { Authorization: `Bearer ${tenantAccessToken}`, },
          params: { dateTimeRenderOption: 'FormattedString' }, // 返回格式化后的日期
        }),
      );
      return response.data.data;
    } catch (error) {
      handleError(this.logger, error, {
        common: '读取电子表格工作表信息失败'
      })
    }
  }

  /** 4. 更新电子表格数据 */
  async updateSheetData(spreadsheetToken: string, range: string, values: any[][], tenantAccessToken: string) {
    try {
      this.logger.log(`${this.updateSheetData.name} was called`);
      const url = `${this.BASE_URL}/sheets/v2/spreadsheets/${spreadsheetToken}/values`;
      const response = await firstValueFrom(
        this.httpService.put(
          url,
          { valueRange: { range, values } },
          { headers: { Authorization: `Bearer ${tenantAccessToken}` } },
        ),
      );
      return response.data;
    } catch (error) {
      handleError(this.logger, error, {
        common: '更新电子表格工作表信息失败'
      })
    }
  }

  /** 获取打卡表数据 */
  async getDailyDataByDate(user: TUser, date = today) {
    try {
      this.logger.log(`${this.getDailyDataByDate.name} was called`);

      const { name } = user
      const tenantAccessToken = await this.getTenantAccessToken(this.app_id, this.app_secret)
      const sheetsList = await this.getSheetInfo(this.spreadsheet_token, tenantAccessToken)
      const sheet_id = sheetsList.find(item => item.title === '②梦想浇水-跟进每日执行情况')?.sheet_id

      // 1. 获取表格数据
      const sheetDataRes = await this.readSheetData(this.spreadsheet_token, sheet_id, tenantAccessToken)
      let sheetData = sheetDataRes.valueRange.values
      sheetData = sheetData.filter(item => item.some(j => j))

      // 2. 找到日期和名称的位置信息
      const dateRowIndex = sheetData.findIndex((row) => compareTime(row[0], date)); // 日期在第一列
      const nameColIndex = sheetData[1]?.findIndex((col) => col === name); // 名称在第二行

      if (nameColIndex === -1) {
        throw new BadRequestException(`名称 "${user.name}" 在表格中未找到`);
      }

      return {
        sheetData,
        nameColIndex,
        dateRowIndex,
        sheet_id,
        tenantAccessToken
      }
    } catch (error) {
      handleError(this.logger, error, {
        common: `获取${date}所有数据失败`
      })
    }

  }


  /** 获取当前日期日常打卡数据 */
  async checkTodayRoutine(req: Request, date = today) {
    try {
      this.logger.log(`${this.checkTodayRoutine.name} was called`);

      const { sheetData, nameColIndex, dateRowIndex } = await this.getDailyDataByDate(req.user as TUser, date)
      if (dateRowIndex === -1 || nameColIndex === -1 || !sheetData.length) {
        return ''
      }
      return sheetData[dateRowIndex][nameColIndex]
    } catch (error) {
      handleError(this.logger, error, {
        common: `获取${date}打卡数据失败`
      })
    }
  }


  /** 5. 日常打卡记录 */
  async dailyRoutine(params: DailyRoutineDto, req: Request) {
    try {
      this.logger.log(`${this.dailyRoutine.name} was called`);

      const { value, date } = params
      const { sheetData, nameColIndex, dateRowIndex, sheet_id, tenantAccessToken } = await this.getDailyDataByDate(req.user as TUser, date)


      // 3. 如果没有此日期，新增一行
      const valueLabel = value ? value : '请假'
      if (dateRowIndex === -1) {
        const newRow = Array(sheetData[1]?.length || 20).fill(''); // 默认列数为 20
        newRow[0] = date; // 第一列填入日期
        newRow[nameColIndex] = valueLabel; // 对应名称列填入数据
        sheetData.push(newRow); // 将新行添加到数据中
      } else {
        // 4. 如果有此日期，直接更新单元格
        sheetData[dateRowIndex][nameColIndex] = valueLabel;
      }
      // 5. 更新表格数据
      const res = await this.updateSheetData(this.spreadsheet_token, sheet_id, sheetData, tenantAccessToken);
      return res
    } catch (error) {
      handleError(this.logger, error, {
        common: '日常打卡失败'
      })
    }
  }
}
